<?php
namespace app\command;

use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Output\OutputInterface;

class Generator extends Command
{
    protected static $defaultName = 'make:generator';
    
    protected function configure()
    {
        $this->setDescription('Generate module code')
             ->addArgument('module', InputArgument::REQUIRED, 'Module name');
    }
    
    protected function execute(InputInterface $input, OutputInterface $output): int
    {
        try {
            $moduleName = $input->getArgument('module');
            $this->generate($moduleName, $output);
            return self::SUCCESS;
        } catch (\Exception $e) {
            $output->writeln("<error>Error: {$e->getMessage()}</error>");
            return self::FAILURE;
        }
    }
    
    public function generate($moduleName, OutputInterface $output)
    {
        try {
            $config = require base_path() . "/modules/{$moduleName}.php";
            
            // 确保目录存在
            $this->ensureDirectoryExists(config('generator.path.model'));
            $this->ensureDirectoryExists(config('generator.path.service'));
            $this->ensureDirectoryExists(config('generator.path.controller'));
            $this->ensureDirectoryExists(config('generator.path.route') . '/admin');
            
            // 生成数据表
            $this->generateTable($config);
            $output->writeln("<info>Table {$config['module']['table']} generated.</info>");
            
            // 生成模型
            $modelPath = config('generator.path.model') . '/' . $this->getClassName($config['module']['name']) . 'Model.php';
            if (file_exists($modelPath)) {
                $output->writeln("<comment>Model {$config['module']['name']} already exists, skipped.</comment>");
            } else {
                $this->generateModel($config);
                $output->writeln("<info>Model {$config['module']['name']} generated.</info>");
            }
            
            // 生成服务层
            $servicePath = config('generator.path.service') . '/' . $this->getClassName($config['module']['name']) . 'Service.php';
            if (file_exists($servicePath)) {
                $output->writeln("<comment>Service {$config['module']['name']} already exists, skipped.</comment>");
            } else {
                $this->generateService($config);
                $output->writeln("<info>Service {$config['module']['name']} generated.</info>");
            }
            
            // 生成控制器
            $controllerPath = config('generator.path.controller') . '/' . $this->getClassName($config['module']['name']) . 'Controller.php';
            if (file_exists($controllerPath)) {
                $output->writeln("<comment>Controller {$config['module']['name']} already exists, skipped.</comment>");
            } else {
                $this->generateController($config);
                $output->writeln("<info>Controller {$config['module']['name']} generated.</info>");
            }
            
            // 生成路由
            $routePath = config('generator.path.route') . '/admin/' . $config['module']['name'] . '.php';
            if (file_exists($routePath)) {
                $output->writeln("<comment>Route {$config['module']['name']} already exists, skipped.</comment>");
            } else {
                $this->generateRoute($config);
                $output->writeln("<info>Route {$config['module']['name']} generated.</info>");
            }
            
        } catch (\Exception $e) {
            $output->writeln("<error>Error: {$e->getMessage()}</error>");
            throw $e;
        }
    }
    
    protected function generateTable($config)
    {
        // 生成主表
        $table = $config['module']['table'];
        $fields = $config['fields'];
        
        // 生成建表SQL
        $sql = "CREATE TABLE IF NOT EXISTS `{$table}` (\n";
        foreach ($fields as $field => $options) {
            $sql .= $this->buildFieldSql($field, $options);
        }
        
        // 添加主键
        foreach ($fields as $field => $options) {
            if (!empty($options['primary'])) {
                $sql .= "    PRIMARY KEY (`{$field}`),\n";
            }
        }
        $sql = rtrim($sql, ",\n") . "\n";
        $sql .= ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;";
        
        // 执行SQL
        \support\Db::connection()->getPdo()->exec($sql);
        
        // 生成关联表
        if (!empty($config['relations'])) {
            foreach ($config['relations'] as $relation) {
                if ($relation['type'] === 'belongsToMany') {
                    $this->createPivotTable($relation);
                }
            }
        }
    }
    
    /**
     * 创建中间表
     */
    protected function createPivotTable($relation)
    {
        $table = $relation['table'];
        
        // 获取关联模型的表名和注释
        $modelClass = $this->getClassName($relation['model']);
        $foreignTable = strtolower(preg_replace('/(?<!^)[A-Z]/', '_$0', $modelClass));
        
        // 根据表名判断是角色-权限表还是管理员-角色表
        $tableType = '';
        $otherKeyComment = '';
        if (strpos($table, 'role_permissions') !== false) {
            $tableType = '角色-权限';
            $otherKeyComment = '权限ID';
        } elseif (strpos($table, 'role_users') !== false) {
            $tableType = '管理员-角色';
            $otherKeyComment = '角色ID';
        }
        
        // 生成建表SQL
        $sql = "CREATE TABLE IF NOT EXISTS `{$table}` (\n";
        $sql .= "    `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '主键ID',\n";
        
        // 外键字段
        if (strpos($table, 'role_permissions') !== false) {
            $sql .= "    `{$relation['foreign_key']}` int(11) NOT NULL COMMENT '角色ID',\n";
            $sql .= "    `{$relation['other_key']}` int(11) NOT NULL COMMENT '权限ID',\n";
        } elseif (strpos($table, 'role_users') !== false) {
            $sql .= "    `{$relation['foreign_key']}` int(11) NOT NULL COMMENT '管理员ID',\n";
            $sql .= "    `{$relation['other_key']}` int(11) NOT NULL COMMENT '角色ID',\n";
        } else {
            $sql .= "    `{$relation['foreign_key']}` int(11) NOT NULL COMMENT '关联{$foreignTable}表ID',\n";
            $sql .= "    `{$relation['other_key']}` int(11) NOT NULL COMMENT '被关联表ID',\n";
        }
        
        // 时间戳字段
        $sql .= "    `created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',\n";
        $sql .= "    `updated_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',\n";
        
        // 索引
        $sql .= "    PRIMARY KEY (`id`),\n";
        $sql .= "    KEY `{$table}_{$relation['foreign_key']}_index` (`{$relation['foreign_key']}`),\n";
        $sql .= "    KEY `{$table}_{$relation['other_key']}_index` (`{$relation['other_key']}`),\n";
        $sql .= "    UNIQUE KEY `{$table}_unique` (`{$relation['foreign_key']}`, `{$relation['other_key']}`)\n";
        
        // 表注释
        $tableComment = $tableType ? "{$tableType}关联表" : "{$table}关联表";
        $sql .= ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='{$tableComment}';";
        
        // 执行SQL
        \support\Db::connection()->getPdo()->exec($sql);
    }
    
    protected function generateModel($config)
    {
        $className = $this->getClassName($config['module']['name']);
        $modelName = $className . 'Model';
        $path = config('generator.path.model') . '/' . $modelName . '.php';
        
        // 检查文件是否已存在
        if (file_exists($path)) {
            return;
        }

        $stub = file_get_contents(config('generator.stub') . '/model.stub');
        
        // 生成关联关系代码
        $relations = '';
        if (!empty($config['relations'])) {
            foreach ($config['relations'] as $name => $relation) {
                $relations .= $this->buildRelation($name, $relation);
            }
        }
        
        // 替换模板变量
        $stub = str_replace(
            [
                '{{namespace}}',
                '{{class}}',
                '{{table}}',
                '{{fillable}}',
                '{{hidden}}',
                '{{casts}}',
                '{{relations}}'  // 添加关联关系占位符
            ],
            [
                config('generator.namespace.model'),
                $modelName,
                $config['module']['table'],
                $this->getFillable($config['fields']),
                $this->getHidden($config['fields']),
                $this->getCasts($config['fields']),
                $relations
            ],
            $stub
        );
        
        // 写入文件
        file_put_contents($path, $stub);
    }
    
    /**
     * 构建关联关系方法
     */
    protected function buildRelation($name, $relation)
    {
        $relationType = $relation['type'];
        $modelClass = $this->getClassName($relation['model']) . 'Model';
        
        $method = "\n    public function {$name}()\n    {\n";
        
        switch ($relationType) {
            case 'belongsToMany':
                $method .= "        return \$this->belongsToMany(\n";
                $method .= "            \\".config('generator.namespace.model')."\\".$modelClass.",\n";
                $method .= "            '".$relation['table']."',\n";
                $method .= "            '".$relation['foreign_key']."',\n";
                $method .= "            '".$relation['other_key']."'\n";
                $method .= "        );\n";
                break;
                
            case 'hasMany':
                $method .= "        return \$this->hasMany(\n";
                $method .= "            \\".config('generator.namespace.model')."\\".$modelClass.",\n";
                $method .= "            '".$relation['foreign_key']."'\n";
                $method .= "        );\n";
                break;
                
            case 'hasOne':
                $method .= "        return \$this->hasOne(\n";
                $method .= "            \\".config('generator.namespace.model')."\\".$modelClass.",\n";
                $method .= "            '".$relation['foreign_key']."'\n";
                $method .= "        );\n";
                break;
                
            case 'belongsTo':
                $method .= "        return \$this->belongsTo(\n";
                $method .= "            \\".config('generator.namespace.model')."\\".$modelClass.",\n";
                $method .= "            '".$relation['foreign_key']."'\n";
                $method .= "        );\n";
                break;
        }
        
        $method .= "    }\n";
        
        return $method;
    }
    
    protected function generateService($config)
    {
        $className = $this->getClassName($config['module']['name']);
        $path = config('generator.path.service') . '/' . $className . 'Service.php';
        
        // 检查文件是否已存在
        if (file_exists($path)) {
            return;
        }

        $stub = file_get_contents(config('generator.stub') . '/service.stub');
        
        // 构建查询条件
        $queryConditions = '';
        foreach ($config['queries'] as $field => $query) {
            $operator = $query[0];
            $value = $query[1];
            $queryConditions .= "        if (!empty(\$params['{$field}'])) {\n";
            $queryConditions .= "            \$query->where('{$field}', '{$operator}', str_replace('?', \$params['{$field}'], '{$value}'));\n";
            $queryConditions .= "        }\n";
        }
        
        // 替换模板变量
        $stub = str_replace(
            [
                '{{namespace}}',
                '{{model_namespace}}',
                '{{model}}',
                '{{class}}',
                '{{query_conditions}}'
            ],
            [
                config('generator.namespace.service'),
                config('generator.namespace.model'),
                $this->getClassName($config['module']['name']) . 'Model',
                $className,
                $queryConditions
            ],
            $stub
        );
        
        // 写入文件
        file_put_contents($path, $stub);
    }
    
    protected function generateController($config)
    {
        $className = $this->getClassName($config['module']['name']);
        $path = config('generator.path.controller') . '/' . $className . 'Controller.php';
        
        // 检查文件是否已存在
        if (file_exists($path)) {
            return;
        }

        $stub = file_get_contents(config('generator.stub') . '/controller.stub');
        
        // 替换模板变量
        $stub = str_replace(
            [
                '{{namespace}}',
                '{{service_namespace}}',
                '{{service}}',
                '{{class}}'
            ],
            [
                config('generator.namespace.controller'),
                config('generator.namespace.service'),
                $this->getClassName($config['module']['name']) . 'Service',
                $className
            ],
            $stub
        );
        
        // 写入文件
        file_put_contents($path, $stub);
    }
    
    protected function generateRoute($config)
    {
        $path = config('generator.path.route') . '/admin/' . $config['module']['name'] . '.php';
        
        // 检查文件是否已存在
        if (file_exists($path)) {
            return;
        }

        $stub = file_get_contents(config('generator.stub') . '/route.stub');
        
        // 替换模板变量
        $stub = str_replace(
            [
                '{{module}}',
                '{{controller}}'
            ],
            [
                $config['module']['name'],
                config('generator.namespace.controller') . '\\' . $this->getClassName($config['module']['name']) . 'Controller'
            ],
            $stub
        );
        
        // 写入文件
        file_put_contents($path, $stub);
    }

    protected function ensureDirectoryExists($path)
    {
        if (!is_dir($path)) {
            mkdir($path, 0755, true);
        }
    }
    
    protected function buildFieldSql($field, $options)
    {
        $sql = "    `{$field}` ";
        
        // 字段类型
        $sql .= $options['type'];
        if (!empty($options['length'])) {
            $sql .= "({$options['length']})";
        }
        
        // 是否可空 (主键必须是 NOT NULL)
        if (!empty($options['primary'])) {
            $sql .= ' NOT NULL';
        } else if (isset($options['nullable'])) {
            $sql .= $options['nullable'] ? ' NULL' : ' NOT NULL';
        } else {
            $sql .= !empty($options['required']) ? ' NOT NULL' : ' NULL';
        }
        
        // 默认值处理
        if (isset($options['default'])) {
            if (!empty($options['raw_default'])) {
                $sql .= " DEFAULT {$options['default']}";  // 不加引号，用于 CURRENT_TIMESTAMP 等
            } else if ($options['type'] !== 'json') {
                $sql .= " DEFAULT " . (is_string($options['default']) ? "'{$options['default']}'" : $options['default']);
            }
        }
        
        // 自增
        if (!empty($options['auto_increment'])) {
            $sql .= " AUTO_INCREMENT";
        }
        
        // 额外SQL（比如 ON UPDATE CURRENT_TIMESTAMP）
        if (!empty($options['extra'])) {
            $sql .= " " . $options['extra'];
        }
        
        // 注释
        if (!empty($options['comment'])) {
            $sql .= " COMMENT '{$options['comment']}'";
        }
        
        return $sql . ",\n";
    }
    
    protected function getFillable($fields)
    {
        $fillable = [];
        foreach ($fields as $field => $options) {
            if ($field !== 'id' && empty($options['auto_increment'])) {
                $fillable[] = $field;
            }
        }
        return "'" . implode("', '", $fillable) . "'";
    }
    
    protected function getHidden($fields)
    {
        $hidden = [];
        foreach ($fields as $field => $options) {
            if (!empty($options['hidden'])) {
                $hidden[] = $field;
            }
        }
        return "'" . implode("', '", $hidden) . "'";
    }
    
    protected function getCasts($fields)
    {
        $casts = [];
        foreach ($fields as $field => $options) {
            if ($options['type'] === 'json') {
                $casts[$field] = 'array';
            } elseif ($options['type'] === 'timestamp') {
                $casts[$field] = 'datetime';
            }
        }
        return var_export($casts, true);
    }
    
    /**
     * 将下划线命名转换为驼峰命名
     */
    protected function getClassName($name)
    {
        return str_replace(' ', '', ucwords(str_replace('_', ' ', $name)));
    }
} 